/* eslint-disable no-console */
import chalk from 'chalk';
import readline from 'readline';

export type LogType = 'error' | 'warn' | 'info';
export type LogLevel = LogType | 'silent';
export interface Logger {
  info: (msg: string, options?: LogOptions) => void;
  warn: (msg: string, options?: LogOptions) => void;
  error: (msg: string, options?: LogOptions) => void;
  clearScreen: (type: LogType) => void;
  hasWarned: boolean;
}

export interface LogOptions {
  clear?: boolean;
  timestamp?: boolean;
}

export const LogLevels: Record<LogLevel, number> = {
  silent: 0,
  error: 1,
  warn: 2,
  info: 3,
};

let lastType: LogType | undefined;
let lastMsg: string | undefined;
let sameCount = 0;

function clearScreen() {
  const repeatCount = process.stdout.rows - 2;
  const blank = repeatCount > 0 ? '\n'.repeat(repeatCount) : '';
  console.log(blank);
  readline.cursorTo(process.stdout, 0, 0);
  readline.clearScreenDown(process.stdout);
}

export interface LoggerOptions {
  prefix?: string;
  allowClearScreen?: boolean;
}

export function createLogger(level: LogLevel = 'info', options: LoggerOptions = {}): Logger {
  const { prefix = '[seepfly]', allowClearScreen = true } = options;

  const thresh = LogLevels[level];
  const clear =
    allowClearScreen && process.stdout.isTTY && !process.env.CI ? clearScreen : () => {};

  function output(type: LogType, msg: string, opts: LogOptions = {}) {
    if (thresh >= LogLevels[type]) {
      const method = type === 'info' ? 'log' : type;
      const format = () => {
        if (opts.timestamp) {
          const tag =
            type === 'info'
              ? chalk.cyan.bold(prefix)
              : type === 'warn'
              ? chalk.yellow.bold(prefix)
              : chalk.red.bold(prefix);
          return `${chalk.dim(new Date().toLocaleTimeString())} ${tag} ${msg}`;
        }
        return msg;
      };
      if (type === lastType && msg === lastMsg) {
        sameCount += 1;
        clear();
        console[method](format(), chalk.yellow(`(x${sameCount + 1})`));
      } else {
        sameCount = 0;
        lastMsg = msg;
        lastType = type;
        if (opts.clear) {
          clear();
        }
        console[method](format());
      }
    }
  }

  const logger: Logger = {
    hasWarned: false,
    info(msg, opts) {
      output('info', msg, opts);
    },
    warn(msg, opts) {
      logger.hasWarned = true;
      output('warn', msg, opts);
    },
    error(msg, opts) {
      logger.hasWarned = true;
      output('error', msg, opts);
    },
    clearScreen(type) {
      if (thresh >= LogLevels[type]) {
        clear();
      }
    },
  };

  return logger;
}
